#pragma once

#include "TableArray.h"

namespace ECS
{
	template <typename T>
	class TiledArrayIterator 
	{
	public:
		TiledArrayIterator() : _data(nullptr), _quater(0), _x(0), _y(0) {}
		TiledArrayIterator(TableArray<T>* data, int quater, int x, int y) :
			_data(data),
			_quater(quater),
			_x(x >= 0 ? x : -1 - x),
			_y(y >= 0 ? y : -1 - y)
		{
		}

		int getX() const
		{
			return _quater < 2 ? _x : -_x - 1;
		}

		int getY() const
		{
			return _quater % 2 ? -_y - 1 : _y;
		}

		bool isValid() const
		{
			return _quater < 4 && _data != nullptr;
		}

		T& operator* () const 
		{ 
			return _data[_quater].get(_x, _y);
		};

		T* operator->() const 
		{ 
			return  &_data[_quater].get(_x, _y);
		}

		TiledArrayIterator operator ++()
		  {
			  doInc();
			  return *this;
		  }
		TiledArrayIterator operator ++(int)
		  {
			  doInc();
			  return *this;
		  }
	protected:
		void doInc()
		{
			_x++;
			if (_x >= _data[_quater].getWidth())
			{
				_x = 0;
				_y++;

				if (_y >= _data[_quater].getHeight())
				{
					_y = 0;
					_quater++;
				}
			}
		}
		TableArray<T>* _data;
		int _quater;
		int _x;
		int _y;
	};

	template <typename T>
	class TiledArray
	{
	public:
		typedef T value_t;
		typedef TiledArrayIterator<T> iterator;
		typedef struct TiledIndex
		{
			int x;
			int y;

			TiledIndex() : x(0), y(0) {}
			TiledIndex(int _x, int _y) : x(_x), y(_y) {}
		} index_t;

		iterator begin();

		int getWidth() const;
		int getHeight() const;

		int getTopY() const;
		int getBottomY() const;
		int getLeftX() const;
		int getRightX() const;

		void resize(int leftX, int bottomY, int rightX, int topY);

		bool isValid(const index_t& index) const { return isValid(index.x, index.y); }
		bool isValid(int x, int y) const;

		const T& at(const index_t& index) const { return at(index.x, index.y); }
		const T& at(int x, int y) const;

		T& operator[] (const index_t& index) { return get(index.x, index.y); }
		T& get(int x, int y) const;
		void set(int x, int y, const T& value);


	protected:
		struct fullindex_t
		{
			int quater;
			int x; 
			int y;

			fullindex_t() {}
			fullindex_t(int _q, int _x, int _y) : quater(_q), x(_x), y(_y) {}
		};

		static fullindex_t getFullIndex(int x, int y)
		{

			if (x >= 0)
			{
				if (y >= 0)
					return fullindex_t(0, x, y);
				else
					return fullindex_t(1, x, -1 - y);
			}
			else
			{
				if (y >= 0)
					return fullindex_t(2, -1 - x, y);
				else
					return fullindex_t(3, -1 - x, -1 - y);
			}
		}

		int _topY;
		int _bottomY;
		int _leftX;
		int _rightX;

		TableArray<T> _data[4];
	};

	template <typename T>
	int TiledArray<T>::getHeight() const
	{
		return _topY - _bottomY + 1;
	}

	template <typename T>
	int TiledArray<T>::getWidth() const
	{
		return _rightX - _leftX + 1;
	}

	template <typename T>
	int TiledArray<T>::getTopY() const
	{
		return this->_topY;
	}

	template <typename T>
	int TiledArray<T>::getBottomY() const
	{
		return this->_bottomY;
	}

	template <typename T>
	int TiledArray<T>::getLeftX() const
	{
		return _leftX;
	}

	template <typename T>
	int TiledArray<T>::getRightX() const
	{
		return _rightX;
	}

	template <typename T>
	T& TiledArray<T>::get(int x, int y) const
	{
		auto index = getFullIndex(x, y);
		return this->_data[index.quater].get(index.x, index.y);
	}

	template <typename T>
	void TiledArray<T>::set(int x, int y, const T& value)
	{
		auto index = getFullIndex(x, y);
		T& ref = get(x, y);
		ref = value;
	}

	template <typename T>
	void TiledArray<T>::resize(int leftX, int bottomY, int rightX, int topY)
	{
		_data[0].resize(rightX + 1, topY + 1);
		_data[1].resize(rightX + 1, -bottomY);
		_data[2].resize(-leftX, topY + 1);
		_data[3].resize(-leftX, -bottomY);

		_bottomY = bottomY;
		_leftX = leftX;
		_rightX = rightX;
		_topY = topY;
	}

	template <typename T>
	bool TiledArray<T>::isValid(int x, int y) const
	{
		return x >= _leftX && x <= _rightX && y >= _bottomY && y <+ _topY;
	}

	template <typename T>
	typename TiledArray<T>::iterator TiledArray<T>::begin()
	{
		return TiledArrayIterator<T>(_data, 0, 0, 0);
	}
}